﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using System.ComponentModel;

namespace Kang.ExtendMethod
{
    public class PropertyEqualsInfo
    {
        public string Name { get; internal set; }
        public object OldValue { get; internal set; }
        public object NewValue { get; internal set; }
    }

    public static class ObjectExtendMethod
    {
        public static string EqualsChangedProperty(this object oldObject, object newObject)
        {
            if (oldObject == null || newObject == null)
            {
                return string.Empty;
            }
            StringBuilder json = new StringBuilder();
            json.Append($"[{Environment.NewLine}");
            Type oType = oldObject.GetType();
            Type nType = newObject.GetType();
            PropertyInfo[] oPropertyInfos = oType.GetProperties();
            PropertyInfo[] nPropertyInfos = nType.GetProperties();
            List<string> oNameList = oPropertyInfos.Select(x => x.Name).ToList();
            List<string> nNameList = nPropertyInfos.Select(x => x.Name).ToList();
            List<string> intersectName = oNameList.Intersect(nNameList).ToList();
            PropertyEqualsInfo propertyEqualsInfo;
            List<PropertyEqualsInfo> propertyEqualsInfoList = new List<PropertyEqualsInfo>();
            foreach (var name in intersectName)
            {
                propertyEqualsInfo = new PropertyEqualsInfo();
                propertyEqualsInfo.Name = name;
                propertyEqualsInfo.OldValue = oPropertyInfos.Where(x => x.Name == name).Select(x => x.GetValue(oldObject, null)).FirstOrDefault();
                propertyEqualsInfo.NewValue = nPropertyInfos.Where(x => x.Name == name).Select(x => x.GetValue(newObject, null)).FirstOrDefault();
                if (!propertyEqualsInfo.OldValue.Equals(propertyEqualsInfo.NewValue))
                {
                    propertyEqualsInfoList.Add(propertyEqualsInfo);
                }
            }
            propertyEqualsInfoList.ForEach(x =>
            {
                json.Append($"  {{{Environment.NewLine}");
                json.Append($"    \"Name\":\"{x.Name}\",{Environment.NewLine}");
                if (x.OldValue is string)
                {
                    json.Append($"    \"OldValue\":\"{x.OldValue}\",{Environment.NewLine}");
                }
                else
                {
                    json.Append($"    \"OldValue\":{x.OldValue},{Environment.NewLine}");
                }
                if (x.NewValue is string)
                {
                    json.Append($"    \"NewValue\":\"{x.NewValue}\"{Environment.NewLine}");
                }
                else
                {
                    json.Append($"    \"NewValue\":\"{x.NewValue}\"{Environment.NewLine}");
                }
                json.Append($"  }},");
            });
            if (propertyEqualsInfoList.Count > 0)
            {
                json.Remove(json.Length - 1, 1);
            }
            json.Append($"{Environment.NewLine}]");
            return json.ToString();
        }

        public static List<PropertyEqualsInfo> EqualsChangedPropertyList(this object oldObject, object newObject)
        {
            if (oldObject == null || newObject == null)
            {
                return null;
            }
            Type oType = oldObject.GetType();
            Type nType = newObject.GetType();
            PropertyInfo[] oPropertyInfos = oType.GetProperties();
            PropertyInfo[] nPropertyInfos = nType.GetProperties();
            List<string> oNameList = oPropertyInfos.Select(x => x.Name).ToList();
            List<string> nNameList = nPropertyInfos.Select(x => x.Name).ToList();
            List<string> intersectName = oNameList.Intersect(nNameList).ToList();
            PropertyEqualsInfo propertyEqualsInfo;
            List<PropertyEqualsInfo> propertyEqualsInfoList = new List<PropertyEqualsInfo>();
            foreach (var name in intersectName)
            {
                propertyEqualsInfo = new PropertyEqualsInfo();
                propertyEqualsInfo.Name = name;
                propertyEqualsInfo.OldValue = oPropertyInfos.Where(x => x.Name == name).Select(x => x.GetValue(oldObject, null)).FirstOrDefault();
                propertyEqualsInfo.NewValue = nPropertyInfos.Where(x => x.Name == name).Select(x => x.GetValue(newObject, null)).FirstOrDefault();
                if (!propertyEqualsInfo.OldValue.Equals(propertyEqualsInfo.NewValue))
                {
                    propertyEqualsInfoList.Add(propertyEqualsInfo);
                }
            }
            return propertyEqualsInfoList;
        }

        public static T CreateNew<T>(this T src)
        {
            if (src == null)
            {
                return default;
            }
            Type srcType = src.GetType();
            PropertyInfo[] srcPropertyInfos = srcType.GetProperties();
            FieldInfo[] srcFieldInfos = srcType.GetFields();
            var des = Activator.CreateInstance(srcType);
            foreach (var p in srcPropertyInfos)
            {
                p.SetValue(des, p.GetValue(src, null), null);
            }
            foreach (var f in srcFieldInfos)
            {
                f.SetValue(des, f.GetValue(src));
            }
            return (T)des;
        }
    }
}
